(function($) {
  
  var methods = $.hierarchy.extend({
    type: 'maps',
    baseClass: "google.maps",
    defaultClass: "Map",
    behaviour: {
      "Map": {
	params: function(obj) {
	  if(obj.params) {
	    return [this, obj.params];
	  } else{
	    return [this];
	  }
	},
	default: function() {
	  var map = this.obj;
	  map.set('overlay', new google.maps.OverlayView());
	  map.get('overlay').draw = function() {};
	  map.get('overlay').setMap(map);
	  google.maps.event.addListener(map, 'idle', function() {
	    this.set('overlayProjection', this.get('overlay').getProjection());
	  });
	}
      },
      "Marker": {
	default: function() {
		  if(this.parent && this.parent.class == 'Map') {
		    this.obj.setMap(this.parent.obj);
		  } else if(this.parent && this.parent.obj && this.parent.obj.getMap && $.type(this.parent.obj.getMap) == 'function') {
			  this.obj.setMap(this.parent.obj.getMap());
		  }
	}
      },
      "InfoWindow": {
	default: function() {
	  if(this.parent && this.parent.class == 'Marker') {
	    var x = this;
	    google.maps.event.addListener(this.parent.obj, 'click', function() {
	      x.obj.open(this.getMap(), this);
	    });
	  }
	}
      },
      "Polygon": {
	//altClass: 'anthill.maps.Polygon',
	default: function() {
	  if(this.parent && this.parent.class == 'Map') {
	    this.obj.setMap(this.parent.obj);
	  } else if(this.parent && this.parent.obj && this.parent.obj.getMap && $.type(this.parent.obj.getMap) == 'function') {
		  this.obj.setMap(this.parent.obj.getMap());
		  var gcObj = this
		  if(this.parent.class == 'Polygon') {
		    var parent = gcObj.parent;
		    if(!parent.polygonChildren) parent.polygonChildren = [];
		    if(parent.polygonChildren.indexOf(this.obj) < 0) parent.polygonChildren.push(this.obj);
		    google.maps.event.addListener(this.obj, 'edit', function(data) {
		      if(data.new && !this.clipping) {
			this.clip({polygon: parent.obj});
			for(var i = 0; i < parent.polygonChildren.length; i++) {
			  if(parent.polygonChildren[i] != this && parent.polygonChildren[i].intersects(this).length) {
			    this.clip({polygon: parent.polygonChildren[i], operation: this.clipOperation.DIFFERENCE});
			  }
			}
		      }
		    });
		  }
	  }
	}
      },
      "Polyline": {
	default: function() {
	  if(this.parent && this.parent.class == 'Map') {
	    this.obj.setMap(this.parent.obj);
	  } else if(this.parent && this.parent.obj && this.parent.obj.getMap && $.type(this.parent.obj.getMap) == 'function') {
		  this.obj.setMap(this.parent.obj.getMap());
	  }
	}
      },
      "Geocoder": {
	altClass: 'anthill.maps.Geocoder',
	default: function() {
	  var gcObj = this;
	  this.obj.geocode(function(responses) {
	    if(gcObj.parent && gcObj.parent.class == 'Map') {
	      for(var i = 0; i<responses.length; i++) {
		if(responses[i].length && responses[i].length > 0) {
		  for(var j = 0; j<responses[i].length; j++) {
		    if(responses[i][j].geometry && responses[i][j].geometry.location) {
		      gcObj.parent.set({
			class: "Marker",
			params: {position: responses[i][j].geometry.location},
			iw: {
			  class: "InfoWindow",
			  params: {content: responses[i][j].formatted_address}
			}
		      });
		    }
		  }
		}
	      }
	    } else if(gcObj.parent && gcObj.parent.class == 'Polyline') {
	      var path = gcObj.parent.obj.getPath();
	      for(var i = 0; i<responses.length; i++) {
		if(responses[i].length && responses[i].length > 0) {
		    if(responses[i][0].geometry && responses[i][0].geometry.location) {
		      path.push(responses[i][0].geometry.location);
		    }
		}
	      }
	    } else if(gcObj.parent && gcObj.parent.class == 'Polygon') {
	      var pts = new google.maps.MVCArray();
	      for(var i = 0; i<responses.length; i++) {
		if(responses[i].length && responses[i].length > 0) {
		  if(responses[i][0].geometry && responses[i][0].geometry.location) {
		    pts.push(responses[i][0].geometry.location);
		  }
		}
	      }
	      var path = gcObj.parent.obj.getPath();
	      for(var i = path.getLength()-1; i>=0; i--) {
		pts.insertAt(0, path.getAt(i));
	      }
	      gcObj.parent.obj.setPath(pts);
	    } else if(gcObj.parent && gcObj.parent.class == 'DirectionsService') {
	      var pts = new google.maps.MVCArray();
	      for(var i = 0; i<responses.length; i++) {
		if(responses[i].length && responses[i].length > 0) {
		  if(responses[i][0].geometry && responses[i][0].geometry.location) {
		    pts.push({location: responses[i][0].geometry.location, stopover: true});
		  }
		}
	      }
	      gcObj.parent.setWaypoints(pts);
	    }
	  });
	}
      },
      "DirectionsService": {
	altClass: 'anthill.maps.DirectionsService',
	default: function() {
	  var gcObj = this;
	  var fnc = null;
	  if(this.parent && this.parent.class == 'Map') {
		  this.obj.route(function(result, status){
		      var mrk = [];
		      var pls = []; 
		      for(var i = 0; i<result.routes.length; i++) {
			var pts = [];
			var route = result.routes[i];
			for(var j = 0; j<route.legs.length; j++) {
			  var leg = route.legs[j];
			  mrk.push({class: "Marker", params: {position: leg.start_location}, iw: {class: "InfoWindow", params: {content: leg.start_address}}});
			  for(var k = 0; k<leg.steps.length; k++) {
			    var step = leg.steps[k];
			    pts.push(step.start_location);
			    for(var l = 0; l<step.path.length; l++) {
			      pts.push(step.path[l]);
			    }
			  }
			  if(j==route.legs.length - 1) {
			    pts.push(step.end_location);
			    mrk.push({class: "Marker", params: {position: leg.end_location}, iw: {class: "InfoWindow", params: {content: leg.end_address}}});
			  }
			}
			pls.push({class: "Polyline", params: {path: pts}});
		      }
		      gcObj.parent.set(pls);
		      gcObj.parent.set(mrk);
		      //methods.searchObj.call(gcObj.context, pls, );
		      //methods.searchObj.call(gcObj.context, mrk, gcObj.parent);
		  });
	  } else if(this.parent && this.parent.class == 'Polygon') {
	    var order = [];
	    var dirs = [];
	    for(var i = 0; i<this.obj.options.waypoints.length; i++) {
	      if($.type(this.obj.options.waypoints[i].location) == 'string') {
		dirs.push(this.obj.options.waypoints[i].location);
		order.push(i);
	      }
	    }
	    if(dirs.length > 0) {
	      var gc = new anthill.maps.Geocoder(dirs);
	      gc.geocode(function(responses) {
		  
		for(var i = 0; i<responses.length; i++) {
			  if(responses[i].length && responses[i].length > 0) {
			      if(responses[i][0].geometry && responses[i][0].geometry.location) {
				gcObj.obj.options.waypoints[order[i]].location = responses[i][0].geometry.location;
			      }
			  }
		}
		
		    var mrk = [];
		for(var i = 0; i<gcObj.obj.options.waypoints.length; i++) {
		    var wp = gcObj.obj.options.waypoints[i];
		    if($.type(wp.location) == 'string') {
			    gcObj.obj.options.waypoints.splice(i,1);
			    i--;
		    } else if(!gcObj.parent.obj.contains(wp.location)){
			    var content = '';
			    for(var j = 0; j<order.length; j++){
				    if(i == order[j]) {
					    content = dirs[j];
					    break;
				    }
			    }
			    if(content == '') {
				    content = "Lat: "+wp.location.lat()+", Lng: "+wp.location.lng();
			    }
			    mrk.push({class: "Marker", params: {position: wp.location}, iw: {class: "InfoWindow", params: {content: content}}});
		    
			    gcObj.obj.options.waypoints.splice(i,1);
			    i--;
		    }
		}
		
		    //methods.searchObj.call(gcObj.context, mrk, gcObj.parent);
		    gcObj.parent.set(mrk);
		      
		gcObj.obj.route(function(result, status){
			      var mrk = [];
			      var pls = []; 
			      for(var i = 0; i<result.routes.length; i++) {
				var pts = [];
				var route = result.routes[i];
				for(var j = 0; j<route.legs.length; j++) {
				  var leg = route.legs[j];
				  mrk.push({class: "Marker", params: {position: leg.start_location}, iw: {class: "InfoWindow", params: {content: leg.start_address}}});
				  for(var k = 0; k<leg.steps.length; k++) {
				    var step = leg.steps[k];
				    pts.push(step.start_location);
				    for(var l = 0; l<step.path.length; l++) {
				      pts.push(step.path[l]);
				    }
				  }
				  if(j==route.legs.length - 1) {
				    pts.push(step.end_location);
				    mrk.push({class: "Marker", params: {position: leg.end_location}, iw: {class: "InfoWindow", params: {content: leg.end_address}}});
				  }
				}
				pls.push({class: "Polyline", params: {path: pts}});
			      }
			      //methods.searchObj.call(gcObj.context, pls, gcObj.parent);
			      //methods.searchObj.call(gcObj.context, mrk, gcObj.parent);
			      gcObj.parent.set(pls);
			      gcObj.parent.set(mrk);
			  });
	      });
	    }
	    
	    fnc = function(result, status){
	    };
	  } else if(this.parent && this.parent.class == 'Polyline') {
	  }
	}
      } 
    }
  });
  
  $[methods.type]={
    contextualize: function(callback) {
      return callback.apply($.extend({root: this}, methods), Array.prototype.slice.call(arguments, 1))
    },
    extend: function(obj) {
      return $.extend({}, methods, obj);
    }
  };
  
  $.fn[methods.type] = function( method ) {    
    if ( methods[method] ) {
      var args = Array.prototype.slice.call(arguments, 1);
      Array.prototype.unshift.call(args, methods[method]);
      return $[methods.type].contextualize.apply(this,  args);
    } else if ( typeof method === 'object' || typeof method === 'array' || ! method ) {
      Array.prototype.unshift.call(arguments, methods.init);
      return $[methods.type].contextualize.apply(this, arguments);
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.'+methods.type);
    }      
  };
})(jQuery);
